import { EventsRes } from '@amcax/base';
import { ctx2 } from '@app-cad/module';
import { calculatePoint, getAngle, isEquals } from '@app-cad/common/utils';
import {
  SKETCH_CENTEREVENTS,
  sketchEventCenter,
} from '@app-cad/environments/nextcad/events/SketchEventCenter';
import { wsApi } from '@app-cad/server';
import { Vector2tuple, Vector3 } from 'three-legacy';
import CmdBase, { PickType, ReturnDataInfo } from '../../CmdBase';
import {
  CircleRelation,
  Constraint,
  DrawMethod,
  MouseState,
  State,
  TrackingData,
} from './types';
import { dimension } from '../dimension/Dimension';

type SnapType = {
  curveId: string;
  pickType: PickType;
  point?: Vector3;
};

export const POLYGON_CHANGE = 'polygonChange';
export default class CmdInteractor extends CmdBase {
  // 临时辅助点
  tempAssistPoint: Vector3;
  trackingData: TrackingData = {
    vertexs: [],
  };
  // 多边形中心点
  private centerPoint: Vector3;
  private radius: number = 0;
  private endPoint: Vector3; // 矩形终点
  private currentState: MouseState = State.NONE;

  private centerSnapInfo: SnapType;
  private endSnapInfo: SnapType;
  constructor() {
    super();
    this.registerEvents();
  }

  _constraint: Constraint;

  public set constraint(value) {
    if (value.edgeCount < 3) {
      return;
    }
    const edgeCount = this._constraint?.edgeCount;
    this._constraint = value;
    if (value.edgeCount != null && edgeCount !== value.edgeCount) {
      this.clearLine();
      this.preview();
    }
    // 输入创建参数
    const flag = value.drawMethod === DrawMethod.Input;
    // 输入圆心
    if (flag) {
      this.reset();
      this.releaseObjects();
    }
    // 圆心有值，则进行绘制
    if (value.circleCenterX != null && value.circleCenterY != null && flag) {
      this.centerPoint = new Vector3(
        value.circleCenterX,
        value.circleCenterY,
        0,
      );
      this.currentState = State.DRAW;
      // 添加辅助点，用来做定点捕捉
      this.createAssistPoint(this.centerPoint, true);
      this.radius = value.circleRadius;
      this.endPoint = this.centerPoint
        .clone()
        .add(new Vector3(this.radius, 0, 0));
      this.preview();
    }
  }

  reset() {
    this.currentState = State.NONE;
    this.assistPoints = [];
    this.radius = 0;
    this.centerPoint = null;
    this.endPoint = null;
    this.tempAssistPoint = null;
    this.trackingData = {
      vertexs: [],
    };
    dimension().destroy();
    this.removeTempPoint();
  }

  registerEvents() {
    dimension().reset();
    sketchEventCenter()
      .setState('cmdSketchPolygon')
      .on(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .on(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .on(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .addExit(this.destroy.bind(this));
  }

  unregisterEvents() {
    sketchEventCenter()
      .setDefault()
      .off(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .off(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .off(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp);
  }

  releaseObjects() {
    this.clearLine();
    this.assisGroup.clear();
    ctx2.viewer.requestRender();
  }

  onMouseMove = (e: EventsRes) => {
    this.setMouseWV(e);
    if (this.currentState === State.DRAW) {
      this.endPoint = this.mouseWV;
      this.isVorH = this.isSamePoint(this.centerPoint, this.endPoint);
      if (this.isVorH) {
        this.creatUpdateLabel(this.endPoint, this.isVorH);
      } else {
        this.showLabel(false);
      }
      this.setRadius();
      this.preview();
    }
  };

  enterKeyDown = () => {
    if (this.currentState === State.DRAW) {
      dimension().determineDim();
      this.setRadius();
      this.preview();
    }
  };
  executeFunc = () => {
    this.finishDraw();
    this.destroy();
  };
  setRadius() {
    if (dimension().dimType === 'create') {
      this.radius = dimension().getDimInfo().value;
      const vec = new Vector3()
        .subVectors(this.endPoint, this.centerPoint)
        .normalize();
      this.endPoint = calculatePoint(vec, this.radius, this.centerPoint);
    } else {
      this.radius = this.endPoint.distanceTo(this.centerPoint);
    }
  }

  onMouseDown = async (e: EventsRes) => {
    if (e.button.onLeft) {
      // 鼠标左键，进行绘制
      if (this.currentState === State.NONE) {
        if (this.pickType !== PickType.Null) {
          this.centerSnapInfo = {
            pickType: this.pickType,
            curveId: this.pickSnapInfo?.id,
            point: this.mouseWV.clone(),
          };
        }
        this.centerPoint = this.mouseWV.clone();
        this.currentState = State.DRAW;
        // 添加辅助点，用来做定点捕捉
        this.createAssistPoint(this.centerPoint, null, null, true);
        this.preview();
      } else if (this.currentState === State.DRAW) {
        if (this.pickType !== PickType.Null) {
          this.endSnapInfo = {
            pickType: this.pickType,
            curveId: this.pickSnapInfo?.id,
            point: this.mouseWV.clone(),
          };
        }
        this.currentState = State.FINISH;
        await this.finishDraw();
        // this.destroy();
        this.cancelOperation();
      }
    } else if (e.button.onRight && !this.closeRightClick) {
      // 鼠标右键
      this.cancelOperation();
    }
  };

  // 取消绘制
  cancelOperation() {
    this.reset();
    this.releaseObjects();
  }

  // 更新点的信息
  updateVertexs = () => {
    // 初始角度
    const mouseAngle =
      this.endPoint && getAngle(this.endPoint, this.centerPoint);
    // 计算多边形的点
    if (
      this._constraint.circleRelation === CircleRelation.circumscribedCircle
    ) {
      //外切多边形
      const points = [];
      const edgeCount = this._constraint.edgeCount;
      const radians = (2 * Math.PI) / edgeCount;
      for (let i = 0; i < edgeCount; i++) {
        const x =
          this.centerPoint.x +
          this.radius * Math.sin(radians * i + Math.PI / 2 - mouseAngle);
        const y =
          this.centerPoint.y +
          this.radius * Math.cos(radians * i + Math.PI / 2 - mouseAngle);
        points.push(new Vector3(x, y, 0));
      }
      points.push(points[0]);
      this.trackingData.vertexs = points;
    } else {
      // 内切多边形
      this.trackingData.vertexs = this.calculatePolygonPoints();
    }
  };

  calculatePolygonPoints() {
    // 初始角度
    const mouseAngle =
      this.endPoint && getAngle(this.endPoint, this.centerPoint);
    const edgeCount = this._constraint.edgeCount;
    const angle = 360 / edgeCount; // 计算多边形的角度
    const points = []; // 存储多边形的点
    const bigRadius = this.radius / Math.cos(((angle / 2) * Math.PI) / 180);
    for (let i = 0; i < edgeCount; i++) {
      const x =
        this.centerPoint.x +
        bigRadius *
          Math.sin(
            (i * angle * Math.PI) / 180 +
              Math.PI / 2 -
              Math.PI / edgeCount -
              mouseAngle,
          ); // 将角度转换为弧度并计算点的x坐标
      const y =
        this.centerPoint.y +
        bigRadius *
          Math.cos(
            (i * angle * Math.PI) / 180 +
              Math.PI / 2 -
              Math.PI / edgeCount -
              mouseAngle,
          ); // 将角度转换为弧度并计算点的y坐标
      points.push(new Vector3(x, y, 0)); // 将点添加到数组中
    }
    points.push(points[0]);
    return points; // 返回多边形的所有点
  }

  preview() {
    if (!this.endPoint || !this.radius) {
      return;
    }
    this.radius > 0 && dimension().dimDiameter(this.centerPoint, this.radius);
    this.updateVertexs();
    this.createUpdateLine(this.trackingData.vertexs, 'line');
    this.createUpdateLine(
      this.getArcVertexs({
        x: this.centerPoint.x,
        y: this.centerPoint.y,
        radius: this.radius,
        startAngle: 0,
        endAngle: Math.PI * 2,
        clockWise: true,
      }),
      'aline',
      1,
    );
    // 计算辅助的点的位置
    this.tempAssistPoint = this.endPoint.clone();
    this.createUpdateLine([this.centerPoint, this.tempAssistPoint], 'rline', 1);
    // 绘制辅助线
    ctx2.viewer.requestRender();
  }

  // 添加约束
  addConstraint() {
    const constraints = [];
    if (this.centerSnapInfo?.curveId) {
      constraints.push(
        this.getConstraint(this.centerSnapInfo, this.centerPoint, false),
      );
    }
    if (this.endSnapInfo?.curveId) {
      constraints.push(
        this.getConstraint(this.endSnapInfo, this.endPoint, false),
      );
    }
    return constraints.filter((item) => item != null);
  }
  async finishDraw() {
    const params = this.getParams();
    if (!params) {
      return;
    }
    const polygonData: ReturnDataInfo = await wsApi.createSketchPolygon(
      this.sketchId,
      params,
    );
    const addLineIds = {
      added: [...polygonData.added],
    };
    await this.render(addLineIds, !!params.annotations[0]);
  }
  getParams() {
    const points = [];
    this.trackingData.vertexs.forEach((item) => {
      points.push([item.x, item.y]);
    });
    const isInscribed =
      this._constraint.circleRelation === CircleRelation.inscribedCircle;
    if (points.length > 0) {
      const point = this.endSnapInfo?.point;
      const curveId = this.endSnapInfo?.curveId;
      let endPoint: Vector2tuple | string = [this.endPoint.x, this.endPoint.y];
      if (
        isEquals(point, this.endPoint) &&
        this.endSnapInfo?.pickType === PickType.Point
      ) {
        endPoint = curveId;
      }
      if (!isInscribed && this.endSnapInfo?.pickType === PickType.Point) {
        points[0] = endPoint;
        points[points.length - 1] = endPoint;
      }
      points.pop();
      return {
        points: points.reverse(),
        curveStyle: this.lineType,
        type: isInscribed ? 'in' : 'out', // 内切圆 外接圆
        constraints: this.addConstraint(),
        annotations: [dimension().addDimension()],
        center: this.centerSnapInfo?.curveId || [
          this.centerPoint.x,
          this.centerPoint.y,
        ],
        point: endPoint,
      };
    }
    return null;
  }
}
